<!DOCTYPE html>
<html lang="zh-cn">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">




<title>高性能网站建设指南 | Miles`s</title>

<link rel="stylesheet" href="https://mada.gitee.io/notes//css/styles.css">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" 
integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/zenburn.min.css" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js" integrity="sha256-/BfiIkHlHoVihZdc6TFuj7MmJ0TWcWsMXkeDFwhi0zw=" crossorigin="anonymous"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="https://mada.gitee.io/notes//js/highlight.js"></script>






<div class="container">
    <nav class="navbar level">
      <div class="navbar-brand">
          <a class="nav-item" href="https://mada.gitee.io/notes/"><h1 class="title is-3">Miles`s</h1></a>
      </div>           
      <div class="navbar-menu has-text-centered is-active">
          <div class="navbar-end is-centered">
              
           </div>
      </div>
    </nav>
  </div>
<div class="container">
  <h2 class="subtitle is-6">November 11, 2016</h2>
  <h1 class="subtitle is-size-4-mobile is-size-3-desktop">高性能网站建设指南</h1>
  <div class="content">
    

<p>本书的主要部分－－14个性能规则。每个规则都进行了介绍，按照优先级顺序，没章一个。并非每个规则都要应用到每个网站，也不是每个网站都应该按同一种角度来分析Web页面。</p>

<h1 id="前端性能的重要性">前端性能的重要性</h1>

<p>只有10%～20%的追踪用户响应时间是花在从Web服务获取HTML文档并传送到浏览器中的。如果希望能够有效的减少页面的响应时间，就必须关注剩余80%～90%的最终用户体验。</p>

<h2 id="跟踪web页面性能">跟踪Web页面性能</h2>

<p>浏览器下载并解析HTML文档。</p>

<h2 id="时间花在哪了">时间花在哪了？</h2>

<p>80%的时间花在了页面的组件上。</p>

<ul>
<li>有缓存的场景并没有太多的下载活动。在这段时间里，浏览器正在解析HTML，javascript和css，并从缓存中获取组件。</li>
<li>大量的HTTP请求并行发生。</li>
<li>在请求脚本时不会发生并行请求。这是因为在很多情况下，浏览器在下载脚本时会阻塞额外的HTTP请求。</li>
</ul>

<h2 id="性能黄金法则">性能黄金法则</h2>

<ul>
<li>大部分网站下载HTML文档的时间不会超过总响应时间的20%，因此关注前端可以很好的提高整体性能。如果我们将后端响应时间缩短一半，整体的响应时间只能减少5%～10%。而如果关注前端性能，同样减少一半，将影响整体响应时间的40%～45%。</li>
<li>改进前端通常只需要较少的时间和资源。（书籍时间较早，可能很少考虑前后端分离的情况，数据的拆解，延迟加载。）</li>
<li>前端性能调整已被证明是可行的。</li>
</ul>

<p>提出14条提升性能的规则。</p>

<ul>
<li>1&ndash;减少HTTP请求</li>
<li>2&ndash;适用内容分发网络</li>
<li>3&ndash;添加Expires头</li>
<li>4&ndash;压缩组件</li>
<li>5&ndash;将样式放在顶部</li>
<li>6&ndash;将脚本放在底部</li>
<li>7&ndash;避免CSS表达式</li>
<li>8&ndash;使用外部JavaScript和CSS</li>
<li>9&ndash;减少DNS查找</li>
<li>10&ndash;精简JavaScript</li>
<li>11&ndash;避免重定向</li>
<li>12&ndash;移除重复脚本</li>
<li>13&ndash;配置ETag</li>
<li>14&ndash;使Ajax可缓存</li>
</ul>

<h1 id="http-概述">HTTP 概述</h1>

<p>Hyper Text Transfer Protocol
是一种客户端/服务器协议，由请求和响应构成。浏览器向一个特定的URL发送http请求，URL对应的宿主服务器发回HTTP响应。和很多Internet服务一样，该协议使用简单的纯文本格式。</p>

<h2 id="压缩">压缩</h2>

<p>可以使用压缩来减小响应的大小。使用<code>Accept-Encoding</code>头来声明它支持压缩。服务器使用<code>Content-Encoding</code>头确认已经被压缩。</p>

<h2 id="条件get请求">条件GET请求</h2>

<p>如果浏览器在其缓存中保留了组件的一个副本，但并不确定它是否仍然有效，就会生成一个条件GET请求。如果确认缓存的副本仍然有效，浏览器就可以使用缓存中的副本，这会得到更小的响应和更快的用户体验。</p>

<p>典型情况下，缓存副本的有效性源自其最后修改时间。基于响应中的<code>last-Modified</code>头，浏览器可以知道组件最后的修改时间。它会使用<code>IF－Modified－Since</code>头将最后修改时间发送给服务器。</p>

<p>如果组件自生成日期以来没有改变过，服务器会返回一个<code>304 Not Modified</code>状态码并不再发送响应体，从而得到一个更小且更快的响应。</p>

<h2 id="expires">Expires</h2>

<p>条件GET请求和304响应有助于让页面加载得更快，但仍需要客户端和服务器之间进行一次往返确认，以执行有效性检查。<code>Expires</code>头痛过明确指出浏览器是否可以使用组件的缓存副本来消除这个需求。</p>

<p>当浏览器看到响应中有一个<code>Expires</code>头时，它会和响应的过期时间组件一起保存到其缓存中。只要组件没有过期。</p>

<h2 id="keep-alive">Keep-Alive</h2>

<p>HTTP构建在<code>Transmission Control Protocol（TCP）</code>之上。在http的早期实现中，每个http请求都要打开一个socket连接。这样做效率很低。<strong>持久连接(Persistent Connection)</strong>的引入解决了多对一请求服务器导致的<code>socket</code>连接低效性的问题。浏览器和服务器使用<code>Connection</code>头来指出对<code>Keep-Alive</code>的支持。</p>

<h1 id="第一章-规则1-减少http请求">第一章  规则1-减少HTTP请求</h1>

<p>这些技术包括图片地图，CSS Sprites，内联图片和脚本，样式表的合并。</p>

<h2 id="图片地图">图片地图</h2>

<p>map area</p>

<p>通过一张图片的不同位置做不同的链接跳转，以减少图片的多次请求。</p>

<pre><code class="language-html">    &lt;img usemap=&quot;#map1&quot; src=&quot;img.jpeg&quot;&gt;
    &lt;map name=&quot;map1&gt;
        &lt;area shape=&quot;rect&quot; coords=&quot;0,0,31,31&quot; href=&quot;a.html&quot; title=&quot;home&quot;&gt; 
        &lt;area shape=&quot;rect&quot; coords=&quot;40,0,31,31&quot; href=&quot;b.html&quot; title=&quot;gifts&quot;&gt; 
    &lt;/map&gt;

</code></pre>

<h2 id="css-sprites">CSS Sprites</h2>

<p>将多个图片平铺到一张图片中。使用CSS的<code>background-position</code>属性，可以将HTML元素放置到图片中期望的位置上。</p>

<p>不同的图片改变background-position的值即可。</p>

<p>图片地图中的图片必须是连续的，而CSS Sprites则没有这个限制。</p>

<p>如果需要在页面中为背景，按钮，导航栏，链接等提供大量的图片，CSS Sprites绝对是一种优秀的解决方案。</p>

<h2 id="内联图片">内联图片</h2>

<p>通过使用<code>data:URL</code>模式在Web页面中包含图片但无需任何额外的HTTP请求。</p>

<p><code>data:[&lt;mediatype&gt;][;base64],&lt;data&gt;</code></p>

<p><code>Base64</code>编码会增加图片的大小，因此整体下载量会增加。</p>

<p>由于data:URL是内联在页面中，在跨越不同的页面时不会被缓存。<strong>不要去内联Logo</strong>，好的做法是实用CSS并将内联图片作为背景。将该CSS规则放在外部样式表中，意味着数据可以缓存在样式表内部。</p>

<pre><code>.logo{
	background-image:url();
}
</code></pre>

<h2 id="合并脚本和样式表">合并脚本和样式表</h2>

<p>一般来说，使用外部脚本和样式表对性能更有利。然而，如果文件太多，会降低性能，因为每个文件都会导致一个额外的http请求。</p>

<p>理想情况下，一个页面应该使用不多余一个的脚本和样式表。（<strong>不考虑缓存？</strong>）</p>

<h1 id="规则2-使用内容发布网络">规则2 使用内容发布网络</h1>

<p>如果应用程序Web服务器离用户更近，则一个HTTP请求的响应时间将缩短。另一方面，如果组件Web服务器离用户更近，则多个HTTP请求的响应时间将缩短。将组建Web服务器分散开，<strong>内容发布网络（Conetent Delivery Network）</strong></p>

<h2 id="内容发布网络-cdn">内容发布网络（CDN）</h2>

<p>内容发布网络是一组发布在多个不同地理位置的Web服务器，用于更加有效地向用户发布内容。通常只在讨论性能问题时会提到它的性能，但它还能节省成本。在优化性能时，向特定用户发布内容的服务器的选择基于对网络可用度的测量。例如，CDN可能选择网络阶跃数最小的服务器，活着具有最短响应时间的服务器。</p>

<h2 id="节省">节省</h2>

<p>讨论使用<code>CDN</code>得到的响应性能上的改善。书中示例将组件存储在CDN上的示例比将所有组件放在一台服务器上加载起来快<code>18%</code>。
如果离Web服务器很近的情况下，可能同一台web服务器更快一点，但是，面对不同的用户，总体来说，还是使用<code>CDN</code>有更好的效果。</p>

<h1 id="规则3-添加-expires-头">规则3 添加<code>Expires</code>头</h1>

<p>最大化的使用浏览器缓存来改善页面的性能。</p>

<h2 id="expires-头">Expires 头</h2>

<p>浏览器（和代理）使用缓存来减少HTTP请求的数量，并减少HTTP响应的大小，使Web页面加载得更快。Web服务器使用<code>Expires</code>头来告诉Web客户端可以使用一个组件的当前副本，直到指定的时间为止。</p>

<h2 id="max-age和mod-expires">Max-age和mod_expires</h2>

<p>Expires问题：要求服务器和客户端的时间严格同步。另外，过期日期需要经常检查，并且一旦到期日到来，还需要服务器提供一个新的日期。
HTTP1.1引入了<code>Cache-Control</code>来克服Expires头的限制。
使用<code>max-age</code>指令指定组件被缓存多久。
以秒为单位定义一个更新窗。如果从组件请求的开始过去的秒数少于<code>max-age</code>，浏览器使用缓存。</p>

<p>如果<code>Expires</code>和<code>max-age</code>同时出现，max-age指令将重写Expires头。</p>

<p><code>mod_expires</code>:使你在使用Expires头时能够像max-age那样以相对的方式设置日前。</p>

<h2 id="空缓存vs完整缓存">空缓存vs完整缓存</h2>

<p>只有用户访问过你的网站之后，长久的Expires头才会对页面浏览器产生影响。第一次访问的时候是空缓存。如果你的页面中的可缓存组件都在缓存中，则缓存是<strong>完整的</strong>。</p>

<h2 id="不仅仅是图片">不仅仅是图片</h2>

<p>理想情况下，页面中的所有组件都应该具有长久的Expires头，并且后续的页面流浪中只需为html文档进行一个http请求。</p>

<h2 id="修订文件名">修订文件名</h2>

<p>服务器有文件更新，希望客户端保持最新的组件。
&gt; 最有效的解决方案是修改其所有链接，这样，全新的请求将从原始服务器下载最新的内容。</p>

<ol>
<li>动态语言生成html文件</li>
<li>在文件名中加入版本号</li>
</ol>

<h1 id="规则4-压缩组件">规则4 压缩组件</h1>

<p>减少http响应的大小。使用<code>gzip</code>编码来压缩http响应包。</p>

<h2 id="压缩是如何工作的">压缩是如何工作的</h2>

<p><code>Accept-Encoding: gzip,deflate</code></p>

<p>如果web服务器看到请求头中有这个头，就会使用gzip来压缩响应。
web服务器通过响应头中的<code>Content-Encoding</code>头来通知客户端。</p>

<p>gzip: 目前最流行和最有效的压缩方法。</p>

<h2 id="压缩什么">压缩什么</h2>

<p>这里只关注脚本和样式表，因为他们用的最普遍。图片和pdf不应该压缩，因为他们本来就已经被压缩了。会浪费cpu资源。</p>

<p>压缩的成本：
1. 服务器话费额外的cpu走起进行压缩
2. 客户端进行解压缩</p>

<p>通常对大于1kb或者2kb的文件进行压缩。
<code>mod-gzip-minimum-file-size</code>指令控制最小值，默认500kb</p>

<h2 id="节省-1">节省</h2>

<p>压缩通常能将响应的数据量减少将近70%。</p>

<h2 id="配置">配置</h2>

<p>apache</p>

<p>nginx</p>

<h2 id="代理缓存">代理缓存</h2>

<p>在服务器的<code>Vary</code>响应头中包含<code>Accept-Encoding</code></p>

<p>这使得代理缓存响应的多个版本，为Accept-Encoding 请求头的每个值缓存一份。</p>

<p>默认情况下<code>mod－gzip</code>会响所有响应添加<code>Vary:Accept-Encoding</code>头，以驱使代理执行正确的操作。</p>

<h2 id="边缘情况">边缘情况</h2>

<p>浏览器支持白名单，由于过于复杂，使用<code>User-Agent</code>作为代理的另外一种评判标准添加到<code>Vary</code>头中。</p>

<p><code>Vary:Accept-Encoding,User-Agent</code></p>

<p>使用<code>Cache-Control:private</code>头来禁用代理缓存。但会增加你的带宽开销，因为代理无法缓存你的内容。</p>

<p>根据带宽开销情况，指定是否经用代理缓存。</p>

<p>ETag：默认情况下，不能反映出内容是否被压缩，因此代理可能会向浏览器提供错误的内容。最好的办法是禁用Etag</p>

<h2 id="压缩的实际效果">压缩的实际效果</h2>

<h1 id="规则5-将样式表放在顶部">规则5 将样式表放在顶部</h1>

<p>问题：将样式表放在前面，会延迟页面中其他重要组件的加载，怎么会改善页面的加载时间呢？</p>

<h2 id="逐步呈现">逐步呈现</h2>

<p>希望浏览器能够尽快的展现内容。
进度指示器的重要性：
&gt; 进度指示器有三个主要优势－－他们让用户知道系统没有崩溃，只是在为他或她解决问题；它们指出了用户能够在漫长的等待中做些其他事情；最后，他们能给用户提供一个可以看的东西，使得等待不再那么无聊。最后一点优势不可低估，这也是为射门推荐使用图形进度条而不是仅仅以数字形式显示预期的剩余时间。</p>

<p>在浏览器和用户等待位于底部的样式表时，浏览器会延迟显示任何可视化组件。</p>

<h2 id="sleep-cgi">sleep.cgi</h2>

<p>用于展示延迟的组件如何影响Web页面。</p>

<h2 id="白屏">白屏</h2>

<h3 id="css-at-the-top">CSS at the Top</h3>

<p>将css放在文档的HEAD中，页面是逐步呈现的。避免了白屏。</p>

<p><code>@import</code>规则会引起白屏现象。
会导致组件下载的无序性。</p>

<h2 id="无样式内容的闪烁">无样式内容的闪烁</h2>

<p>白屏现象源自于浏览器的行为。尽管IE已经得到了所需的组件，它依然要等到样式表下载完毕之后再呈现它们。样式表在页面中的位置并不影响它们的下载时间，但是会影响页面的呈现。</p>

<p>无样式内容的闪烁：当页面逐步加载完成，绘制完成，最后下载样式表并解析，对已经呈现的样式进行重绘。这就是<code>无样式内容的闪烁</code>。</p>

<p>白屏是浏览器在尝试修改前端工程师所犯的错误－将样式表放在文档比较靠后的位置。是对FOUC(Flash ofUnstyled Content)问题的弥补。</p>

<h2 id="前端工程师应该做什么">前端工程师应该做什么？</h2>

<p>将样式表放在顶部HEAD中。</p>

<h1 id="将脚本放在底部">将脚本放在底部</h1>

<p>保持页面逐步呈现，也可以提高下载的并行度。</p>

<h2 id="脚本带来的问题">脚本带来的问题</h2>

<ol>
<li>脚本阻塞了并行下载</li>
<li>在脚本以下的内容，逐步呈现被阻塞。</li>
</ol>

<h2 id="并行下载">并行下载</h2>

<p>http1.1规范，该规范建议浏览器从每个足迹名并行地下载两个组件。
如果一个web页面平均地将其组件分别放在两个主机名下，整体响应时间将可以减少大约一半。</p>

<p>但是过多的主机名并行下载反而会降低性能
Yahoo的研究表明，使用两个主机名比使用1，4，10个主机名能带来更好的性能。</p>

<h2 id="脚本阻塞下载">脚本阻塞下载</h2>

<p>脚本阻塞并行下载，浏览器也不回启动其他下载。其中一个原因是，脚本可能使用<code>document.write</code>来修改页面内容，因此浏览器会等待，以确保页面能够恰当的布局。（现代浏览器以及ie8+支持预下载）</p>

<p>另一个原因是：保证脚本能够按照正确的顺序执行。</p>

<h2 id="将脚本放在底部-1">将脚本放在底部</h2>

<p>（没有很好的描述很多复杂的情况。略过）</p>

<h1 id="规则7-避免css表达式">规则7 避免CSS表达式</h1>

<h2 id="updating-expressions">Updating Expressions</h2>

<h1 id="使用外部javascript和css">使用外部JavaScript和CSS</h1>

<p>是外部还是内联，通常使用外部文件更好</p>

<h2 id="纯粹而言-内联快一些">纯粹而言，内联快一些</h2>

<p>HTTP请求带来的开销，但是结合缓存，总体来说还是外部样式更快。</p>

<p>关键因素是：文档请求的数量相关。外部文档被缓存的频率。</p>

<h3 id="页面查看">页面查看</h3>

<p>比如用户每个月只访问一次，使用内联，缓存的作用不是太大。</p>

<h3 id="空缓存vs完整缓存-1">空缓存vs完整缓存</h3>

<h3 id="组件重用">组件重用</h3>

<p>每个页面都使用了相同的javascript和css。使用外部文件提高重用率。</p>

<h2 id="主页">主页</h2>

<p>倾向使用内联而不是外部文件。</p>

<h2 id="两全其美">两全其美</h2>

<h3 id="加载后下载">加载后下载</h3>

<p>在<code>onload</code>之后，动态的加载额外需要的外部文档文件。
<code>document.write</code>,或者加载一个不可见的iframe</p>

<h3 id="动态内联">动态内联</h3>

<p>视频cookie判断是否被缓存</p>

<h1 id="较少dns查找">较少DNS查找</h1>

<p>Internet是通过IP地址来查询服务器的，由于ip地址很难记忆，通常使用包含主机名的URL来取代它，但当浏览器发送其请求时，IP地址仍然是必须的。这就是<code>Domain Name System（DNS）</code>所处的角色。
然而DNS也是开销。通常浏览器查找一个给定的主机名IP地址要花费20-120毫秒。在DNS查找完成之后，浏览器不能从主机名那里下载到任何东西。响应时间依赖于DNS解析起，它所承担的请求压力，你与它之间的距离和你的带宽速度。</p>

<h2 id="dns缓存和ttl">DNS缓存和TTL</h2>

<p>在用户请求一个主机之后，DNS信息会留在炒作系统的DNS缓存中，之后对于该主机名的请求无需进行过多的DNS查找，至少段时间内不需要。</p>

<p>很多浏览器拥有其自己的缓存，和操作系统的缓存相分离。只要浏览器在其缓存中保留了DNS记录，它就不会麻烦操作系统来请求这个记录。</p>

<p>使事情更复杂的是，设计者知道IP地址会变化以及缓存会消耗内存。因此，应该周期性的消除缓存中的DNS记录，并通过大量不同的配置检测清除的频率有多高。</p>

<h2 id="影响dns缓存的因素">影响DNS缓存的因素</h2>

<p>缓存的存活时间（Time-to-live TTL)值。</p>

<p>尽管操作系统会设置该值，但是浏览器通常忽略该值，并设置它自己的时间限制。此外，<code>Keep-Alive</code>特性可以同时覆盖TTL和浏览器的时间限制。</p>

<p>浏览器对缓存的数量会有限制，较早的记录会被丢弃。</p>

<h2 id="ttl值">TTL值</h2>

<p>通常1分钟－1小时之间。</p>

<h2 id="减少dns查找">减少DNS查找</h2>

<p>作者建议至少2个，但不要超过4个主机名，既减少DNS查找，又可以提高并行下载。</p>

<h1 id="规则10-精简javascript">规则10 精简JavaScript</h1>

<h2 id="精简">精简</h2>

<p>从代码中移除不必要的字符以减小其大小，进而改善加载时间的实践。</p>

<h2 id="混淆">混淆</h2>

<p>改写代码</p>

<h2 id="节省-2">节省</h2>

<h2 id="内联脚本">内联脚本</h2>

<h1 id="避免重定向">避免重定向</h1>

<p><strong>重定向（Redirect）</strong>用于将用户从一个URL重新路由到另外一个URL。重定向有很多种－－301和302是最常用的两种。</p>

<p>作用：
* 网站的重新设计
* 跟踪流量
* 记录广告点击量
* 建立易于记忆的URL</p>

<h2 id="重定向的类型">重定向的类型</h2>

<p>3xx的状态码</p>

<ul>
<li>300 Multiple Choices</li>
<li>301 Moved Permancently</li>
<li>302 Moved Temporarily(Found)</li>
<li>303 See Other(对302的说明)</li>
<li>304 Not Modified</li>
<li>305 Use Proxy</li>
<li>306 (不再使用)</li>
<li>307 Temporary Redirect(对302的说明)</li>
</ul>

<p><strong>304 Not Modified</strong>并不真的重定向－－它用来响应条件GET请求，避免下载已经存在于浏览器缓存中的数据。
<strong>306</strong> 已经被废弃</p>

<p>浏览器会自动将用户带到<code>Location</code>字段所给出的URL。重定向所需的所有信息都出现在这个头中。响应体通常是空的。不管叫什么名字，301和302响应在实际中都不会被缓存，除非有附加的头－－Expires和Cache-Control等要求它这么做</p>

<p><code>&lt;meta http-equiv=&quot;refresh&quot; content=&quot;0&quot;; url=http://www.baidu.com&gt;</code>
meta指定重定向。</p>

<p>javascript设置重定向<code>document.location</code>设置为期望的URL</p>

<h2 id="重定向是如何损伤性能的">重定向是如何损伤性能的</h2>

<h2 id="避免重定向-1">避免重定向</h2>

<p>alias
mod_rewrite
DirectorySlash
直接链接代码</p>

<h1 id="规则12-移除重复脚本">规则12 移除重复脚本</h1>

<h1 id="规则13-配置etag">规则13 配置ETag</h1>

<p>ETag可能会阻碍缓存。</p>

<h2 id="etag是什么">ETag是什么</h2>

<p><strong>实体标签（Entity Tag）</strong>是web服务器和浏览器用于确认缓存组件的有效性的一种机制。</p>

<p>用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配。</p>

<p>是唯一标识了一个组件的一个特定版本的字符串。唯一的格式约束是该字符串必须用引号引起来。</p>

<p>此后，如果浏览器必须验证一个组件，它会使用<code>If-None-Match</code>头奖ETag传回原始服务器。如果ETag是匹配的，就会返回304状态码。</p>

<h2 id="etag带来的问题">ETag带来的问题</h2>

<ol>
<li>对于服务器集群来说，ETag通常是不匹配的。</li>
<li>代理，</li>
</ol>

<p><code>If-None-Match</code>比<code>If-Modified-Since</code>具有更高的优先级。但是，http1.1规范如果请求头中出现了这两个头，必须全部一致，不然不会返回304</p>

<h2 id="etag-用还是不用">ETag－用还是不用</h2>

<h1 id="规则14-使ajax可缓存">规则14 使Ajax可缓存</h1>

<h1 id="案例">案例</h1>

<p><img src="../../images/performance/google-1.png" alt="google" />
<img src="../../images/performance/google-2.png" alt="google" />
<img src="../../images/performance/google-3.png" alt="google" />
<img src="../../images/performance/google-4.png" alt="google" />
<img src="../../images/performance/google-5.png" alt="google" /></p>

  </div>
</div>
<div class="container has-text-centered">
    
</div>

<div class="container has-text-centered">
  
</div>
<section class="section">
  <div class="container has-text-centered">
    <p></p>
  </div>
</section>


